﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Ext.h".
//-------------------------------------------------------------------

#include "GLShader.h"
#include "GLObjectManager.h"

namespace UX_STL
{
namespace GLShell
{

//---------------------------- GLShader ---------------------------//
/*
=================================================
	конструктор
=================================================
*/
	GLShader::GLShader(): _uID(0), _eType(gl_shader::_UNKNOWN), _bCompiled(false)
	{
	}

/*
=================================================
	деструктор
=================================================
*/
	GLShader::~GLShader()
	{
		if ( _uID != 0 )
		{
			GLOM()->DeleteShader( _uID );
			_uID = 0;
		}
	}
	
/*
=================================================
	поменять местами данные класса
=================================================
*/
	void GLShader::Swap(GLShader &cShader)
	{
		_swap( this->_bCompiled,	cShader._bCompiled );
		_swap( this->_eType,		cShader._eType );
		_swap( this->_uID,			cShader._uID );
	}

/*
=================================================
	создать шейдер
=================================================
*/
	bool GLShader::Create(gl_shader::type eType)
	{
		Destroy();
		CHECK_ERR( GLOM()->CreateShader( eType, _uID ) );
		_eType = eType;
		return true;
	}
	
/*
=================================================
	удалить шейдер
=================================================
*/
	void GLShader::Destroy()
	{
		if ( _uID != 0 )
		{
			GLOM()->DeleteShader( _uID );
			_uID = 0;
		}
		_eType		= gl_shader::_UNKNOWN;
		_bCompiled	= false;
	}
	
/*
=================================================
	передать исходник шейдера
=================================================
*/
	bool GLShader::SetSource(const char *pSource, usize uLength) const
	{
		CHECK_ERR( pSource != null );
		CHECK_ERR( _uID != 0 );

		GLint len = (GLint) uLength;

		GL_CALL_R( glShaderSource( _uID, 1, (const GLchar **) &pSource, (const GLint *)(len == 0 ? null : &len) ) );
		return true;
	}
	
/*
=================================================
	передать исходник шейдера
=================================================
*/
	bool GLShader::SetSource(const uni_array< const char *> &aSource) const
	{
		CHECK_ERR( !aSource.Empty() );
		CHECK_ERR( _uID != 0 );

		GL_CALL_R( glShaderSource( _uID, (GLsizei) aSource.Count(), (const GLchar **) aSource.ptr(), null ) );
		return true;
	}
	
/*
=================================================
	возвращает исходник шейдера
=================================================
*/
	bool GLShader::GetSource(string &sSource) const
	{
		GLint	i_length = 0;
		GLsizei	i_len	 = 0;
		GL_CALL_R( glGetShaderiv( _uID, GL_SHADER_SOURCE_LENGTH, &i_length ) );

		sSource.Reserve( i_length + 2 );

		GL_CALL_R( glGetShaderSource( _uID, i_length+2, &i_len, sSource.ptr() ) );
		sSource.SetLength( i_len );

		return true;
	}
	
/*
=================================================
	устанавливает бинарник шейдера
=================================================
*/
	bool GLShader::SetBinary(const void *pData, usize uDataSize, GLenum eFormat)
	{
		CHECK_ERR( pData != null and uDataSize > 0 );
		CHECK_ERR( _uID != 0 );

		GL_CALL_R( glShaderBinary( 1, &_uID, eFormat, pData, (GLsizei)uDataSize ) );
		
		GLint	i_res = 0;
		GL_CALL_R( glGetShaderiv( _uID, GL_COMPILE_STATUS,  &i_res ) );
		_bCompiled = (i_res == GL_TRUE);

		CHECK_ERR( _bCompiled and "Shader binary compiled with errors." );
		return true;
	}
	
/*
=================================================
	компилирует шейдер
=================================================
*/
	bool GLShader::Compile()
	{
		CHECK_ERR( _uID != 0 );
		GL_CALL_R( glCompileShader( _uID ) );
		
		GLint	i_res = 0;
		GL_CALL_R( glGetShaderiv( _uID, GL_COMPILE_STATUS,  &i_res ) );
		_bCompiled = (i_res == GL_TRUE);

		CHECK_ERR( _bCompiled and "Shader compiled with errors." );
		return true;
	}
	
/*
=================================================
	возвращает лог компиляции шейдера
=================================================
*/
	bool GLShader::GetCompilationLog(string &sLog) const
	{
		GLint	i_logsize = 0;
		GL_CALL_R( glGetShaderiv( _uID, GL_INFO_LOG_LENGTH, &i_logsize ) );

		if ( i_logsize > 0 )
		{
			static const char *	s_aPrefixes[] = {	"Unknown shader type ",
													"Vertex shader ",
													"Fragment shader ",
													"Geometry shader ",
													"Tess control shader ",
													"Tess evaluation shader ",
													"Compute shader " };

			const char *	p_prefix = s_aPrefixes[0];

			switch ( _eType )
			{
				case gl_shader::VERTEX :			p_prefix = s_aPrefixes[1];	break;
				case gl_shader::FRAGMENT :			p_prefix = s_aPrefixes[2];	break;
				case gl_shader::GEOMETRY :			p_prefix = s_aPrefixes[3];	break;
				case gl_shader::TESS_CONTROL :		p_prefix = s_aPrefixes[4];	break;
				case gl_shader::TESS_EVALUATION :	p_prefix = s_aPrefixes[5];	break;
				case gl_shader::COMPUTE :			p_prefix = s_aPrefixes[6];	break;
			};

			string	s_info;
			s_info.Reserve( i_logsize + 1 );

			GL_CALL_R( glGetShaderInfoLog( _uID, i_logsize, null, s_info.ptr() ) );

			s_info.SetLength( i_logsize );

			sLog << p_prefix;

			if ( _bCompiled )	sLog << "compilation message:\n";
			else				sLog << "compilation error:\n";
			
			sLog << s_info.ptr();
		}

		return true;
	}
	
/*
=================================================
	возвращает массив поддерживаемых бинарных форматов
=================================================
*/
	bool GLShader::s_GetBinaryFormats(array<GLenum> &aFormats)
	{
		GLint	i_num = 0;
		GL_CALL_R( glGetIntegerv( GL_NUM_SHADER_BINARY_FORMATS, &i_num ) );

		aFormats.Resize( i_num, false );
		GL_CALL_R( glGetIntegerv( GL_SHADER_BINARY_FORMATS, (GLint *) aFormats.ptr() ) );
		return true;
	}
	
/*
=================================================
	возвращает информацию о диапазоне и точности
	заданного формата
=================================================
*/
	bool GLShader::s_GetPrecisionFormat(gl_shader::type eType, gl_shader_precision::type ePrecision, ivec2 &vRange, int &iPrecision)
	{
		GL_CALL_R( glGetShaderPrecisionFormat( eType, ePrecision, vRange.ptr(), &iPrecision ) );
		return true;
	}


	
//---------------------------- GLProgram --------------------------//
/*
=================================================
	конструктор
=================================================
*/
	GLProgram::GLProgram():
		_uID(0), _eAttached(gl_shader::_UNKNOWN_BIT), _bLinked(false),
		_bSeparable(false), _bRetrievable(false)
	{
	}

/*
=================================================
	деструктор
=================================================
*/
	GLProgram::~GLProgram()
	{
		if ( _uID != 0 )
		{
			GLOM()->DeleteProgram( _uID );
			_uID = 0;
		}
	}

/*
=================================================
	поменять местами данные классов
=================================================
*/
	void GLProgram::Swap(GLProgram &cProgram)
	{
		bool	tmp;
		UX_SWAP( tmp, this->_bLinked,		cProgram._bLinked );
		UX_SWAP( tmp, this->_bRetrievable,	cProgram._bRetrievable );
		UX_SWAP( tmp, this->_bSeparable,	cProgram._bSeparable );
		_swap( this->_eAttached,			cProgram._eAttached );
		_swap( this->_uID,					cProgram._uID );
	}

/*
=================================================
	создать программу
=================================================
*/
	bool GLProgram::Create()
	{
		Destroy();
		CHECK_ERR( GLOM()->CreateProgram( _uID ) );
		return true;
	}

/*
=================================================
	удалить программу
=================================================
*/
	void GLProgram::Destroy()
	{
		if ( _uID != 0 )
		{
			GLOM()->DeleteProgram( _uID );
			_uID = 0;
		}
		_eAttached		= gl_shader::_UNKNOWN_BIT;
		_bLinked		= false;
		_bSeparable		= false;
		_bRetrievable	= false;
	}

/*
=================================================
	слинковать шейдеры программы
=================================================
*/
	bool GLProgram::Link()
	{
		CHECK_ERR( _uID != 0 );
		GL_CALL_R( glLinkProgram( _uID ) );
		
		GLint	i_res = 0;
		GL_CALL( glGetProgramiv( _uID, GL_LINK_STATUS, &i_res ) );
		_bLinked = (i_res == GL_TRUE);
		return _bLinked;
	}
	
/*
=================================================
	возвращает лог линковки программы
=================================================
*/
	bool GLProgram::GetLinkingLog(string &sLog) const
	{
		GLint	i_logsize	= 0;
		GL_CALL_R( glGetProgramiv( _uID, GL_INFO_LOG_LENGTH, &i_logsize ) );

		if ( i_logsize > 0 )
		{
			string	s_info;
			s_info.Reserve( i_logsize + 1 );

			GL_CALL_R( glGetProgramInfoLog( _uID, i_logsize, null, s_info.ptr() ) );

			s_info.SetLength( i_logsize );
			
			if ( _bLinked )	sLog << "program linking message:\n";
			else			sLog << "program linking error:\n";

			sLog << uni_c_string( s_info.ptr(), i_logsize );
		}

		return true;
	}

/*
=================================================
	валидация программы,
	в случае возникновения ошибок или предупреждений
	запишется информация лог
=================================================
*/
	bool GLProgram::Validate(string *pLog, bool bErrorsOnly)
	{
		CHECK_ERR( _uID != 0 and _bLinked );
		GL_CALL_R( glValidateProgram( _uID ) );

		GLint	i_res		= 0,
				i_logsize	= 0;
		bool	b_valid		= false;

		GL_CALL( glGetProgramiv( _uID, GL_VALIDATE_STATUS, &i_res ) );
		b_valid = (i_res == GL_TRUE);
		
		if ( (pLog == null or bErrorsOnly) and b_valid )
			return true;
		
		if ( pLog != null )
		{
			GL_CALL( glGetProgramiv( _uID, GL_INFO_LOG_LENGTH, &i_logsize ) );

			if ( i_logsize > 0 )
			{
				string	s_info;
				s_info.SetLength( i_logsize + 1 );

				GL_CALL( glGetProgramInfoLog( _uID, i_logsize, null, s_info.ptr() ) );

				s_info.SetLength( i_logsize );
			
				if ( b_valid )	*pLog << "program validation message:\n";
				else			*pLog << "program validation error:\n";
			
				*pLog << s_info.ptr();
			}
		}

		return b_valid;
	}
	
/*
=================================================
	устанавливает флаги:
	bSeparable - позволяет присоединять к program pipeline
	bRetrievable - позволяет извлекать бинарник программы
=================================================
*/
	bool GLProgram::SetPrameters(bool bSeparable, bool bRetrievable)
	{
		CHECK_ERR( _uID != 0 );

		bool	ret = true;

		if ( C_GL_ARB_separate_shader_objects and !_bLinked )
		{
			bool	res = true;
			GL_CALL_B( glProgramParameteri( _uID, GL_PROGRAM_SEPARABLE, bSeparable ? GL_TRUE : GL_FALSE ), res );
			res ? _bSeparable = bSeparable : 0;
			ret &= res;
		}

		if ( C_GL_ARB_get_program_binary )
		{
			bool	res = true;
			GL_CALL_B( glProgramParameteri( _uID, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, bRetrievable ? GL_TRUE : GL_FALSE ), res );
			res ? _bRetrievable = bRetrievable : 0;
			ret &= res;
		}

		return ret;
	}

/*
=================================================
	устанавливает бинарную программу
=================================================
*/
	bool GLProgram::SetBinary(const void *pData, usize uDataSize, GLenum eFormat)
	{
		CHECK_ERR( _uID != 0 );
		CHECK_ERR( C_GL_ARB_get_program_binary );

		GL_CALL_R( glProgramBinary( _uID, eFormat, pData, (GLsizei)uDataSize ) );
		
		GLint	i_res = 0;
		GL_CALL( glGetProgramiv( _uID, GL_LINK_STATUS, &i_res ) );
		_bLinked = (i_res == GL_TRUE);
		return true;
	}

/*
=================================================
	присоединить шейдер
=================================================
*/
	bool GLProgram::Attach(const GLShader *pShader)
	{
		CHECK_ERR( _uID != 0 );
		ASSERT( pShader->Type() != gl_shader::COMPUTE or _eAttached == 0 );
		
		GL_CALL_R( glAttachShader( _uID, pShader->Id() ) );
		_eAttached |= gl_shader::TypeToBit( pShader->Type() );
		return true;
	}

/*
=================================================
	отсоединить шейдер
=================================================
*/
	bool GLProgram::Detach(const GLShader *pShader)
	{
		CHECK_ERR( _uID != 0 );

		GL_CALL_R( glDetachShader( _uID, pShader->Id() ) );
		_eAttached &= ~ gl_shader::TypeToBit( pShader->Type() );
		return true;
	}

/*
=================================================
	возвращает массив с информацией об аттрибутах
=================================================
*/
	bool GLProgram::GetAttributes(attrib_array_t &aAttribs) const
	{
		CHECK_ERR( _uID != 0 and _bLinked );

		GLint	i_attrib_count = 0,
				i_max_length = 0;

		GL_CALL_R( glGetProgramiv( _uID, GL_ACTIVE_ATTRIBUTES,				&i_attrib_count ) );
		GL_CALL_R( glGetProgramiv( _uID, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH,	&i_max_length ) );

		aAttribs.Resize( i_attrib_count, false );

		i_max_length += 1;

		string		s_buf;
		GLint		i_size = 0,
					i_length = 0;
		GLenum		e_type = 0;

		s_buf.Reserve( i_max_length+1 );

		FOR( i, aAttribs )
		{
			GL_CALL_R( glGetActiveAttrib( _uID, (GLuint)i, (GLsizei)i_max_length, &i_length, &i_size, &e_type, s_buf.ptr() ) );
			s_buf.SetLength( i_length );

			aAttribs[i].uSize	= i_size;
			aAttribs[i].eType	= (gl_uniform::type)e_type;
			aAttribs[i].sName	= s_buf;
			aAttribs[i].uIndex	= GetAttrib( aAttribs[i].sName.cstr() );
		}
		return true;
	}
	
/*
=================================================
	cut symbols: "[0]"
=================================================
*/
	void GLProgram::_s_CutArray(string &sStr)
	{
		if ( sStr.Back() != ']' )
			return;

		FOR_rev( i, sStr )
		{
			if ( sStr[i] == '[' ) {
				sStr.SetLength( i );
				return;
			}
		}
	}

/*
=================================================
	возвращает массив с информацией о юниформах
=================================================
*/
	bool GLProgram::GetUniforms(uniform_array_t &aUniforms) const
	{
		CHECK_ERR( _uID != 0 and _bLinked );

		GLint	i_unif_count = 0,
				i_max_length = 0;

		GL_CALL_R( glGetProgramiv( _uID, GL_ACTIVE_UNIFORMS,			&i_unif_count ) );
		GL_CALL_R( glGetProgramiv( _uID, GL_ACTIVE_UNIFORM_MAX_LENGTH,	&i_max_length ) );

		aUniforms.Resize( i_unif_count, false );

		i_max_length += 2;
		
		string			s_buf;
		GLint			i_size = 0,
						i_length = 0;
		GLenum			e_type = 0;
		GLint			i_blockidx	= 0,
						i_offset	= 0;

		s_buf.Reserve( i_max_length+1 );

		FOR( i, aUniforms )
		{
			GLuint	u_idx = (GLuint) i;

			GL_CALL_R( glGetActiveUniform( _uID, (GLuint)i, (GLsizei)i_max_length, &i_length, &i_size, &e_type, s_buf.ptr() ) );
			GL_CALL_R( glGetActiveUniformsiv( _uID, 1, &u_idx, GL_UNIFORM_BLOCK_INDEX, &i_blockidx ) );
			GL_CALL_R( glGetActiveUniformsiv( _uID, 1, &u_idx, GL_UNIFORM_OFFSET,		&i_offset ) );

			s_buf.SetLength( i_length );

			aUniforms[i].uBlockIndex = i_blockidx;
			aUniforms[i].uCount		 = i_size;
			aUniforms[i].uOffset	 = i_offset;
			aUniforms[i].eType		 = (gl_uniform::type)e_type;
			aUniforms[i].sName		 = s_buf;

			if ( aUniforms[i].sName.Back() == ']' )
				_s_CutArray( aUniforms[i].sName );
			
			aUniforms[i].uIndex		 = i_blockidx == -1 ? GetUniform( aUniforms[i].sName.cstr() ) : -1;
		}
		return true;
	}
	
/*
=================================================
	возвращает информацию о юниформе по индексу
=================================================
*/
	bool GLProgram::GetUniformInfo(GLint location, OUT GLUniformInfo &sInfo) const
	{
		CHECK_ERR( _uID != 0 and _bLinked );

		GLint	i_unif_count = 0,
				i_max_length = 0;

		GL_CALL_R( glGetProgramiv( _uID, GL_ACTIVE_UNIFORMS,			&i_unif_count ) );
		GL_CALL_R( glGetProgramiv( _uID, GL_ACTIVE_UNIFORM_MAX_LENGTH,	&i_max_length ) );

		i_max_length += 2;
		
		string			s_buf;			s_buf.Reserve( i_max_length );
		GLint			i_size = 0,
						i_length = 0;
		GLenum			e_type = 0;
		GLint			i_blockidx	= 0,
						i_offset	= 0;

		for (int i = 0; i < i_unif_count; ++i)
		{
			GLuint	u_idx = i;

			GL_CALL_R( glGetActiveUniform( _uID, (GLuint)i, (GLsizei)i_max_length, &i_length, &i_size, &e_type, s_buf.ptr() ) );
			GL_CALL_R( glGetActiveUniformsiv( _uID, 1, &u_idx, GL_UNIFORM_BLOCK_INDEX,	&i_blockidx ) );
			GL_CALL_R( glGetActiveUniformsiv( _uID, 1, &u_idx, GL_UNIFORM_OFFSET,		&i_offset ) );

			if ( i_blockidx != -1 )
				continue;

			s_buf.SetLength( i_length );

			if ( s_buf.Back() == ']' )
				_s_CutArray( s_buf );
			
			const GLint	loc = GetUniform( s_buf.ptr() );

			if ( loc == location )
			{
				sInfo.uBlockIndex	= i_blockidx;
				sInfo.uCount		= i_size;
				sInfo.uOffset		= i_offset;
				sInfo.eType			= (gl_uniform::type)e_type;
				sInfo.sName			= s_buf;
				sInfo.uIndex		= loc;
				return true;
			}
		}
		return false;
	}

/*
=================================================
	возвращает массив с информацией о юниформах текстур
=================================================
*/
	bool GLProgram::GetSamplers(sampler_array_t &aSamplers) const
	{
		CHECK_ERR( _uID != 0 and _bLinked );

		GLint	i_unif_count = 0,
				i_max_length = 0;

		GL_CALL_R( glGetProgramiv( _uID, GL_ACTIVE_UNIFORMS,			&i_unif_count ) );
		GL_CALL_R( glGetProgramiv( _uID, GL_ACTIVE_UNIFORM_MAX_LENGTH, &i_max_length ) );

		aSamplers.Resize( i_unif_count, false );

		i_max_length += 2;
		
		string			s_buf;
		GLint			i_size = 0,
						i_length = 0;
		GLenum			e_type = 0;
		GLint			i_blockidx	= 0,
						i_offset	= 0;

		s_buf.Reserve( i_max_length+1 );

		FOR( i, aSamplers )
		{
			gl_texture::type		e_tex		= gl_texture::_UNKNOWN;
			gl_tex_component::type	e_comp		= gl_tex_component::NONE;
			bool					b_shadow	= false;
			GLuint					u_idx		= (GLuint) i;

			GL_CALL_R( glGetActiveUniform( _uID, (GLuint)i, (GLsizei)i_max_length, &i_length, &i_size, &e_type, s_buf.ptr() ) );
			
			if ( !s_ConvertSamplerToTarget( (gl_uniform::type)e_type, e_tex, e_comp, b_shadow ) )
				continue;
			
			GL_CALL_R( glGetActiveUniformsiv( _uID, 1, &u_idx, GL_UNIFORM_BLOCK_INDEX, &i_blockidx ) );
			GL_CALL_R( glGetActiveUniformsiv( _uID, 1, &u_idx, GL_UNIFORM_OFFSET,		&i_offset ) );

			s_buf.SetLength( i_length );

			aSamplers[i].uBlockIndex = i_blockidx;
			aSamplers[i].uCount		 = i_size;
			aSamplers[i].uOffset	 = i_offset;
			aSamplers[i].eTarget	 = e_tex;
			aSamplers[i].eType		 = e_comp;
			aSamplers[i].bShadow	 = b_shadow;
			aSamplers[i].sName		 = s_buf;

			if ( aSamplers[i].sName.Back() == ']' )
				_s_CutArray( aSamplers[i].sName );
			
			aSamplers[i].uIndex		 = i_blockidx == -1 ? GetUniform( aSamplers[i].sName.cstr() ) : -1;
		}
		return true;
	}
	
/*
=================================================
	конвертация сэмплера в тип(цель) текстуры
	и обратно
=================================================
*/
	struct TSamplerTarget
	{
		gl_uniform::type		eSampler;
		gl_texture::type		eTarget;
		gl_tex_component::type	eType;
		bool					bShadow;
	};

	static const TSamplerTarget		s_aSamplerTargets[] =
	{
		{ gl_uniform::SAMPLER_1D,						gl_texture::TEXTURE_1D,						gl_tex_component::FLOAT,	false },
		{ gl_uniform::SAMPLER_2D,						gl_texture::TEXTURE_2D,						gl_tex_component::FLOAT,	false },
		{ gl_uniform::SAMPLER_3D,						gl_texture::TEXTURE_3D,						gl_tex_component::FLOAT,	false },
		{ gl_uniform::SAMPLER_CUBE_MAP,					gl_texture::TEXTURE_CUBE_MAP,				gl_tex_component::FLOAT,	false },
		{ gl_uniform::SAMPLER_1D_SHADOW,				gl_texture::TEXTURE_1D,						gl_tex_component::FLOAT,	true  },
		{ gl_uniform::SAMPLER_2D_SHADOW,				gl_texture::TEXTURE_2D,						gl_tex_component::FLOAT,	true  },
		{ gl_uniform::SAMPLER_2D_MS,					gl_texture::TEXTURE_2D_MULTISAMPLE,			gl_tex_component::FLOAT,	false },
		{ gl_uniform::SAMPLER_2D_MS_ARRAY,				gl_texture::TEXTURE_2D_MULTISAMPLE_ARRAY,	gl_tex_component::FLOAT,	false },
		{ gl_uniform::SAMPLER_CUBE_MAP_ARRAY,			gl_texture::TEXTURE_CUBE_MAP_ARRAY,			gl_tex_component::FLOAT,	false },
		{ gl_uniform::SAMPLER_CUBE_MAP_ARRAY_SHADOW,	gl_texture::TEXTURE_CUBE_MAP_ARRAY,			gl_tex_component::FLOAT,	true  },
		{ gl_uniform::SAMPLER_1D_ARRAY,					gl_texture::TEXTURE_1D_ARRAY,				gl_tex_component::FLOAT,	false },
		{ gl_uniform::SAMPLER_2D_ARRAY,					gl_texture::TEXTURE_2D_ARRAY,				gl_tex_component::FLOAT,	false },
		{ gl_uniform::SAMPLER_1D_ARRAY_SHADOW,			gl_texture::TEXTURE_1D_ARRAY,				gl_tex_component::FLOAT,	true  },
		{ gl_uniform::SAMPLER_2D_ARRAY_SHADOW,			gl_texture::TEXTURE_2D_ARRAY,				gl_tex_component::FLOAT,	true  },
		{ gl_uniform::SAMPLER_CUBE_MAP_SHADOW,			gl_texture::TEXTURE_CUBE_MAP,				gl_tex_component::FLOAT,	true  },
		{ gl_uniform::SAMPLER_BUFFER,					gl_texture::TEXTURE_BUFFER,					gl_tex_component::FLOAT,	false },
		{ gl_uniform::SAMPLER_2D_RECT,					gl_texture::TEXTURE_RECTANGLE,				gl_tex_component::FLOAT,	false },
		{ gl_uniform::SAMPLER_2D_RECT_SHADOW,			gl_texture::TEXTURE_RECTANGLE,				gl_tex_component::FLOAT,	true  },
		{ gl_uniform::INT_SAMPLER_1D,					gl_texture::TEXTURE_1D,						gl_tex_component::INT,		false },
		{ gl_uniform::INT_SAMPLER_2D,					gl_texture::TEXTURE_2D,						gl_tex_component::INT,		false },
		{ gl_uniform::INT_SAMPLER_3D,					gl_texture::TEXTURE_3D,						gl_tex_component::INT,		false },
		{ gl_uniform::INT_SAMPLER_CUBE_MAP,				gl_texture::TEXTURE_CUBE_MAP,				gl_tex_component::INT,		false },
		{ gl_uniform::INT_SAMPLER_1D_ARRAY,				gl_texture::TEXTURE_1D,						gl_tex_component::INT,		false },
		{ gl_uniform::INT_SAMPLER_2D_ARRAY,				gl_texture::TEXTURE_2D,						gl_tex_component::INT,		false },
		{ gl_uniform::INT_SAMPLER_BUFFER,				gl_texture::TEXTURE_BUFFER,					gl_tex_component::INT,		false },
		{ gl_uniform::INT_SAMPLER_2D_RECT,				gl_texture::TEXTURE_RECTANGLE,				gl_tex_component::INT,		false },
		{ gl_uniform::INT_SAMPLER_CUBE_MAP_ARRAY,		gl_texture::TEXTURE_CUBE_MAP_ARRAY,			gl_tex_component::INT,		false },
		{ gl_uniform::INT_SAMPLER_2D_MS,				gl_texture::TEXTURE_2D_MULTISAMPLE,			gl_tex_component::INT,		false },
		{ gl_uniform::INT_SAMPLER_2D_MS_ARRAY,			gl_texture::TEXTURE_2D_MULTISAMPLE_ARRAY,	gl_tex_component::INT,		false },
		{ gl_uniform::UINT_SAMPLER_1D,					gl_texture::TEXTURE_1D,						gl_tex_component::UINT,		false },
		{ gl_uniform::UINT_SAMPLER_2D,					gl_texture::TEXTURE_2D,						gl_tex_component::UINT,		false },
		{ gl_uniform::UINT_SAMPLER_3D,					gl_texture::TEXTURE_3D,						gl_tex_component::UINT,		false },
		{ gl_uniform::UINT_SAMPLER_CUBE_MAP,			gl_texture::TEXTURE_CUBE_MAP,				gl_tex_component::UINT,		false },
		{ gl_uniform::UINT_SAMPLER_1D_ARRAY,			gl_texture::TEXTURE_1D,						gl_tex_component::UINT,		false },
		{ gl_uniform::UINT_SAMPLER_2D_ARRAY,			gl_texture::TEXTURE_2D,						gl_tex_component::UINT,		false },
		{ gl_uniform::UINT_SAMPLER_BUFFER,				gl_texture::TEXTURE_BUFFER,					gl_tex_component::UINT,		false },
		{ gl_uniform::UINT_SAMPLER_2D_RECT,				gl_texture::TEXTURE_RECTANGLE,				gl_tex_component::UINT,		false },
		{ gl_uniform::UINT_SAMPLER_CUBE_MAP_ARRAY,		gl_texture::TEXTURE_CUBE_MAP_ARRAY,			gl_tex_component::UINT,		false },
		{ gl_uniform::UINT_SAMPLER_2D_MS,				gl_texture::TEXTURE_2D_MULTISAMPLE,			gl_tex_component::UINT,		false },
		{ gl_uniform::UINT_SAMPLER_2D_MS_ARRAY,			gl_texture::TEXTURE_2D_MULTISAMPLE_ARRAY,	gl_tex_component::UINT,		false }
	};


	bool GLProgram::s_ConvertSamplerToTarget(gl_uniform::type eSampler, gl_texture::type &eTarget, gl_tex_component::type &eType, bool &bShadow)
	{
		for (usize i = 0; i < CountOf( s_aSamplerTargets ); ++i) {
			if ( s_aSamplerTargets[i].eSampler == eSampler ) {
				eTarget = s_aSamplerTargets[i].eTarget;
				eType   = s_aSamplerTargets[i].eType;
				bShadow	= s_aSamplerTargets[i].bShadow;
				return true;
			}
		}
		return false;
	}
	
	bool GLProgram::s_ConvertTargetToSampler(gl_texture::type eTarget, gl_tex_component::type eType, bool bShadow, gl_uniform::type &eSampler)
	{
		for (usize i = 0; i < CountOf( s_aSamplerTargets ); ++i) {
			if ( s_aSamplerTargets[i].eTarget == eTarget and
				 s_aSamplerTargets[i].eType   == eType and
				 s_aSamplerTargets[i].bShadow == bShadow )
			{
				eSampler = s_aSamplerTargets[i].eSampler;
				return true;
			}
		}
		return false;
	}
	
/*
=================================================
	возвращает массив с информацией о юниформах
	находящихся в блоке
=================================================
*/
	bool GLProgram::GetBlockUniforms(uint uIndex, uniform_array_t &aUniforms) const
	{
		CHECK_ERR( _uID != 0 and _bLinked );

		GLint	i_unif_count = 0,
				i_max_length = 0;

		GL_CALL_R( glGetProgramiv( _uID, GL_ACTIVE_UNIFORM_MAX_LENGTH, &i_max_length ) );
		GL_CALL_R( glGetActiveUniformBlockiv( _uID, uIndex, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &i_unif_count ) );
	
		int32_array_t	a_indices( i_unif_count );
	
		GL_CALL_R( glGetActiveUniformBlockiv( _uID, uIndex, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, a_indices.ptr() ) );

		aUniforms.Resize( i_unif_count, false );

		i_max_length += 2;
		
		string			s_buf;
		GLint			i_size = 0,
						i_length = 0;
		GLenum			e_type = 0;
		GLint			i_offset	= 0;

		s_buf.Reserve( i_max_length+1 );

		FOR( i, a_indices )
		{
			GLuint	u_idx	= a_indices[i];

			GL_CALL_R( glGetActiveUniform( _uID, (GLuint)i, (GLsizei)i_max_length, &i_length, &i_size, &e_type, s_buf.ptr() ) );
			GL_CALL_R( glGetActiveUniformsiv( _uID, 1, &u_idx, GL_UNIFORM_OFFSET,	&i_offset ) );

			s_buf.SetLength( i_length );

			aUniforms[i].uIndex		 = -1;
			aUniforms[i].uBlockIndex = uIndex;
			aUniforms[i].uCount		 = i_size;
			aUniforms[i].uOffset	 = i_offset;
			aUniforms[i].eType		 = (gl_uniform::type)e_type;
			aUniforms[i].sName		 = s_buf;

			if ( aUniforms[i].uCount > 1 )
				_s_CutArray( aUniforms[i].sName );
		}
		return true;
	}

/*
=================================================
	возвращает массив с информацией о блоках юниформ
=================================================
*/
	bool GLProgram::GetUniformBlocks(uniformblock_array_t &aUBlocks) const
	{
		CHECK_ERR( _uID != 0 and _bLinked );

		GLint	i_ublocks_count	= 0,
				i_max_length	= 0;

		GL_CALL_R( glGetProgramiv( _uID, GL_ACTIVE_UNIFORM_BLOCKS,					&i_ublocks_count ) );
		GL_CALL_R( glGetProgramiv( _uID, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH,	&i_max_length ) );

		aUBlocks.Resize( i_ublocks_count, false );

		i_max_length += 2;
		
		string			s_buf;
		GLint			i_size = 0,
						i_length = 0;

		s_buf.Reserve( i_max_length+1 );

		FOR( i, aUBlocks )
		{
			GL_CALL_R( glGetActiveUniformBlockName( _uID, (GLuint)i, (GLsizei)i_max_length, &i_length, s_buf.ptr() ) );
			GL_CALL_R( glGetActiveUniformBlockiv( _uID, (GLuint)i, GL_UNIFORM_BLOCK_DATA_SIZE, &i_size ) );

			s_buf.SetLength( i_length );

			aUBlocks[i].uSize	= i_size;
			aUBlocks[i].sName	= s_buf;
			aUBlocks[i].uIndex	= GetUniformBlockIndex( aUBlocks[i].sName.cstr() );
		}
		return true;
	}
	
/*
=================================================
	возвращает массив с информацией о униформах подпрограмм
=================================================
*/
	bool GLProgram::GetSubroutineUniforms(subroutine_uniform_array_t &aUniforms) const
	{
		CHECK_ERR( _uID != 0 and _bLinked );
		
		GLint					i_count  = 0,
								i_length = 0;
		string					s_buf;
		GLSubroutineUniformInfo	s_info;

		for (gl_shader::index i = gl_shader::_IDX_FIRST; i < gl_shader::_IDX_COUNT; ++i)
		{
			const GLenum	e_type = gl_shader::IndexToType( i );

			GL_CALL_R( glGetProgramStageiv( _uID, e_type, GL_ACTIVE_SUBROUTINE_UNIFORMS, &i_count ) );
			GL_CALL_R( glGetProgramStageiv( _uID, e_type, GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH, &i_length ) );

			s_buf.Reserve( i_length+1 );

			for (int j = 0; j < i_count; ++j)
			{
				GLsizei		i_str_len = 0;
				GLint		i_temp	  = 0;
				GL_CALL_R( glGetActiveSubroutineUniformName( _uID, e_type, j, (GLsizei) s_buf.Size(), &i_str_len, (GLchar *)s_buf.ptr() ) );
				GL_CALL_R( glGetActiveSubroutineUniformiv( _uID, e_type, j, GL_NUM_COMPATIBLE_SUBROUTINES, &i_temp ) );

				s_buf.SetLength( i_str_len );
				
				s_info.sName	= s_buf;
				s_info.uIndex	= glGetSubroutineUniformLocation( _uID, e_type, s_info.sName.cstr() );
				s_info.eShader	= (gl_shader::type)e_type;
				s_info.aIndices.Resize( i_temp, false );

				GL_CALL_R( glGetActiveSubroutineUniformiv( _uID, e_type, j, GL_COMPATIBLE_SUBROUTINES, (GLint *)s_info.aIndices.ptr() ) );
				GL_CALL_R( glGetActiveSubroutineUniformiv( _uID, e_type, j, GL_UNIFORM_SIZE, &i_temp ) );

				s_info.uSize = i_temp;
				
				aUniforms.PushBack( s_info );
			}
		}
		return true;
	}
	
/*
=================================================
	возвращает массив с информацией подпрограммах
=================================================
*/
	bool GLProgram::GetSubroutines(subroutine_array_t &aSubroutines) const
	{
		CHECK_ERR( _uID != 0 and _bLinked );
		
		GLint				i_count  = 0,
							i_length = 0;
		int8_array_t		a_buf;
		GLSubroutineInfo	s_info;

		for (gl_shader::index i = gl_shader::_IDX_FIRST; i < gl_shader::_IDX_COUNT; ++i)
		{
			const GLenum	e_type = gl_shader::IndexToType( i );

			GL_CALL_R( glGetProgramStageiv( _uID, e_type, GL_ACTIVE_SUBROUTINES, &i_count ) );
			GL_CALL_R( glGetProgramStageiv( _uID, e_type, GL_ACTIVE_SUBROUTINE_MAX_LENGTH, &i_length ) );

			a_buf.Resize( i_length );

			for (int j = 0; j < i_count; ++j)
			{
				GLsizei		i_str_len = 0;
				GL_CALL_R( glGetActiveSubroutineName( _uID, e_type, j, (GLsizei) a_buf.Size(), &i_str_len, (GLchar *)a_buf.ptr() ) );

				s_info.sName.Copy( uni_c_string( (const char *)a_buf.ptr(), (usize)i_str_len ) );
				s_info.uIndex  = glGetSubroutineIndex( _uID, e_type, s_info.sName.cstr() );
				s_info.eShader = (gl_shader::type)e_type;
				aSubroutines.PushBack( s_info );
			}
		}
		return true;
	}

/*
=================================================
	возвращает скомпилированную программу
=================================================
*/
	bool GLProgram::GetBinary(binary_array_t &aBuffer, GLenum &eFormat) const
	{
		CHECK_ERR( C_GL_ARB_get_program_binary );
		CHECK_ERR( _uID != 0 and _bLinked and _bRetrievable );

		GLint	i_length = 0;
		GL_CALL_R( glGetProgramiv( _uID, GL_PROGRAM_BINARY_LENGTH, &i_length ) );

		aBuffer.Resize( i_length, false );
		GL_CALL_R( glGetProgramBinary( _uID, (GLsizei)aBuffer.Size(), (GLsizei*)&i_length, &eFormat, aBuffer.ptr() ) );
		return true;
	}
	
/*
=================================================
	возвращает присоединенные шейдеры
=================================================
*/
	bool GLProgram::GetAttachedShaders(shader_array_t &aShaders) const
	{
		GLsizei	i_count = 0;

		GL_CALL_R( glGetProgramiv( _uID, GL_ATTACHED_SHADERS, &i_count ) );

		array<GLuint>	a_shaders( i_count );

		GL_CALL_R( glGetAttachedShaders( _uID, (GLsizei) a_shaders.Count(), &i_count, a_shaders.ptr() ) );

		aShaders.ClearMem();

		for (int i = 0; i < i_count; ++i)
		{
			GLint	i_type = 0;
			GL_CALL_R( glGetShaderiv( a_shaders[i], GL_SHADER_TYPE, &i_type ) );

			aShaders.PushBack( GLAttachedShader( a_shaders[i], (gl_shader::type)i_type ) );
		}
		return true;
	}
	
/*
=================================================
	обновляет биты присоединенных шейдеров
=================================================
*/
	bool GLProgram::UpdateAttachment()
	{
		GLsizei	i_count = 0;

		GL_CALL_R( glGetProgramiv( _uID, GL_ATTACHED_SHADERS, &i_count ) );

		array<GLuint>	a_shaders( i_count );

		GL_CALL_R( glGetAttachedShaders( _uID, (GLsizei) a_shaders.Count(), &i_count, a_shaders.ptr() ) );
		
		_eAttached = gl_shader::_UNKNOWN_BIT;

		for (int i = 0; i < i_count; ++i)
		{
			GLint	i_type = 0;
			GL_CALL_R( glGetShaderiv( a_shaders[i], GL_SHADER_TYPE, &i_type ) );

			_eAttached |= gl_shader::TypeToBit( (gl_shader::type)i_type );
		}
		return true;
	}
	
/*
=================================================
	возвращает список поддерживаемых бинарных форматов
=================================================
*/
	bool GLProgram::s_GetBinaryFormats(array<GLenum> &aFormats)
	{
		GLint	i_num = 0;
		GL_CALL_R( glGetIntegerv( GL_NUM_PROGRAM_BINARY_FORMATS, &i_num ) );

		aFormats.Resize( i_num, false );
		GL_CALL_R( glGetIntegerv( GL_PROGRAM_BINARY_FORMATS, (GLint *) aFormats.ptr() ) );
		
		return true;
	}
	
/*
=================================================
	возвращает размер юниформы
=================================================
*/
	struct TUniformTypeInfo
	{
		gl_uniform::type	eType;
		gl_uniform::type	eBaseType;
		uint				uCount;
		uint				uSizeof;
	};

	static const TUniformTypeInfo	s_aUniformTypeInfos[] =
	{
		{ gl_uniform::BOOL,				gl_uniform::BOOL,		1,		4		},
		{ gl_uniform::BOOL_VEC2,		gl_uniform::BOOL,		2,		4*2		},
		{ gl_uniform::BOOL_VEC3,		gl_uniform::BOOL,		3,		4*3		},
		{ gl_uniform::BOOL_VEC4,		gl_uniform::BOOL,		4,		4*4		},
		{ gl_uniform::FLOAT,			gl_uniform::FLOAT,		1,		4		},
		{ gl_uniform::FLOAT_VEC2,		gl_uniform::FLOAT,		2,		4*2		},
		{ gl_uniform::FLOAT_VEC3,		gl_uniform::FLOAT,		3,		4*3		},
		{ gl_uniform::FLOAT_VEC4,		gl_uniform::FLOAT,		4,		4*4		},
		{ gl_uniform::FLOAT_MAT2,		gl_uniform::FLOAT,		2*2,	4*2*2	},
		{ gl_uniform::FLOAT_MAT3,		gl_uniform::FLOAT,		3*3,	4*3*3	},
		{ gl_uniform::FLOAT_MAT4,		gl_uniform::FLOAT,		4*4,	4*4*4	},
		{ gl_uniform::FLOAT_MAT2x3,		gl_uniform::FLOAT,		2*3,	4*2*3	},
		{ gl_uniform::FLOAT_MAT2x4,		gl_uniform::FLOAT,		2*4,	4*2*4	},
		{ gl_uniform::FLOAT_MAT3x2,		gl_uniform::FLOAT,		3*2,	4*3*2	},
		{ gl_uniform::FLOAT_MAT3x4,		gl_uniform::FLOAT,		3*4,	4*3*4	},
		{ gl_uniform::FLOAT_MAT4x2,		gl_uniform::FLOAT,		4*2,	4*4*2	},
		{ gl_uniform::FLOAT_MAT4x3,		gl_uniform::FLOAT,		4*3,	4*4*3	},
		{ gl_uniform::DOUBLE,			gl_uniform::DOUBLE,		1,		8		},
		{ gl_uniform::DOUBLE_VEC2,		gl_uniform::DOUBLE,		2,		8*2		},
		{ gl_uniform::DOUBLE_VEC3,		gl_uniform::DOUBLE,		3,		8*3		},
		{ gl_uniform::DOUBLE_VEC4,		gl_uniform::DOUBLE,		4,		8*4		},
		{ gl_uniform::DOUBLE_MAT2,		gl_uniform::DOUBLE,		2*2,	8*2*2	},
		{ gl_uniform::DOUBLE_MAT3,		gl_uniform::DOUBLE,		3*3,	8*3*3	},
		{ gl_uniform::DOUBLE_MAT4,		gl_uniform::DOUBLE,		4*4,	8*4*4	},
		{ gl_uniform::DOUBLE_MAT2x3,	gl_uniform::DOUBLE,		2*3,	8*2*3	},
		{ gl_uniform::DOUBLE_MAT2x4,	gl_uniform::DOUBLE,		2*4,	8*2*4	},
		{ gl_uniform::DOUBLE_MAT3x2,	gl_uniform::DOUBLE,		3*2,	8*3*2	},
		{ gl_uniform::DOUBLE_MAT3x4,	gl_uniform::DOUBLE,		3*4,	8*3*4	},
		{ gl_uniform::DOUBLE_MAT4x2,	gl_uniform::DOUBLE,		4*2,	8*4*2	},
		{ gl_uniform::DOUBLE_MAT4x3,	gl_uniform::DOUBLE,		4*3,	8*4*3	},
		{ gl_uniform::INT,				gl_uniform::INT,		1,		4		},
		{ gl_uniform::INT_VEC2,			gl_uniform::INT,		2,		4*2		},
		{ gl_uniform::INT_VEC3,			gl_uniform::INT,		3,		4*3		},
		{ gl_uniform::INT_VEC4,			gl_uniform::INT,		4,		4*4		},
		{ gl_uniform::UINT,				gl_uniform::UINT,		1,		4		},
		{ gl_uniform::UINT_VEC2,		gl_uniform::UINT,		2,		4*2		},
		{ gl_uniform::UINT_VEC3,		gl_uniform::UINT,		3,		4*3		},
		{ gl_uniform::UINT_VEC4,		gl_uniform::UINT,		4,		4*4		}
	};

	usize GLProgram::s_SizeofUniform(gl_uniform::type eType)
	{
		for (usize i = 0; i < CountOf( s_aUniformTypeInfos ); ++i) {
			if ( s_aUniformTypeInfos[i].eType == eType ) {
				return s_aUniformTypeInfos[i].uSizeof;
			}
		}
		WARNING( "unknown uniform type (warning: sampler and image uniforms not supported!)" );
		return 0;
	}
	
/*
=================================================
	возвращает базовый тип юниформы и количество элементов
=================================================
*/
	bool GLProgram::s_GetUniformBaseType(gl_uniform::type eType, gl_uniform::type &eBase, uint &uCount)
	{
		for (usize i = 0; i < CountOf( s_aUniformTypeInfos ); ++i) {
			if ( s_aUniformTypeInfos[i].eType == eType ) {
				eBase	= s_aUniformTypeInfos[i].eBaseType;
				uCount	= s_aUniformTypeInfos[i].uCount;
				return true;
			}
		}
		WARNING( "unknown uniform type (warning: sampler and image uniforms not supported!)" );
		return false;
	}


	
//------------------------ GLProgramPipeline ----------------------//
/*
=================================================
	конструктор
=================================================
*/
	GLProgramPipeline::GLProgramPipeline(): _uID(0), _eStages(gl_shader::_UNKNOWN_BIT)
	{
		CHECK( C_GL_ARB_separate_shader_objects );
	}

/*
=================================================
	деструктор
=================================================
*/
	GLProgramPipeline::~GLProgramPipeline()
	{
		if ( _uID != 0 )
		{
			GLOM()->DeleteProgramPipelines( 1, &_uID );
			_uID = 0;
		}
	}
	
/*
=================================================
	поменять местами данные класса
=================================================
*/
	void GLProgramPipeline::Swap(GLProgramPipeline &cPipeline)
	{
		_swap( this->_eStages,	cPipeline._eStages );
		_swap( this->_uID,		cPipeline._uID );
	}

/*
=================================================
	создать пайплайн
=================================================
*/
	bool GLProgramPipeline::Create()
	{
		Destroy();
		CHECK_ERR( GLOM()->GenProgramPipelines( 1, &_uID ) );
		return true;
	}

/*
=================================================
	удалить pipeline
=================================================
*/
	void GLProgramPipeline::Destroy()
	{
		if ( _uID != 0 )
		{
			GLOM()->DeleteProgramPipelines( 1, &_uID );
			_uID = 0;
		}
		_eStages = gl_shader::_UNKNOWN_BIT;
	}
	
/*
=================================================
	проверка...
=================================================
*/
	bool GLProgramPipeline::Validate(string *pLog, bool bErrorsOnly) const
	{
		CHECK_ERR( _uID != 0 );

		GL_CALL( glValidateProgramPipeline( _uID ) );

		GLint	i_status = 0;
		GL_CALL_R( glGetProgramPipelineiv( _uID, GL_VALIDATE_STATUS, &i_status ) );

		if ( i_status == GL_TRUE and ( bErrorsOnly or pLog == null ) )
			return true;

		CHECK_ERR( GetInfoLog( *pLog ) );
		return true;
	}

/*
=================================================
	возвращает лог
=================================================
*/
	bool GLProgramPipeline::GetInfoLog(string &sLog) const
	{
		CHECK_ERR( _uID != 0 );

		GLint	i_length = 0;
		GL_CALL_R( glGetProgramPipelineiv( _uID, GL_INFO_LOG_LENGTH, &i_length ) );

		if ( i_length > 0 )
		{
			sLog.Reserve( i_length + 1 );
			GL_CALL_R( glGetProgramPipelineInfoLog( _uID, i_length, null, sLog.ptr() ) );

			sLog[i_length] = '\0';
		}
		else
			sLog.ClearMem();
		
		return true;
	}

/*
=================================================
	возвращает присоединенные программы
=================================================
*/
	bool GLProgramPipeline::GetAttachedPrograms(attachedprogs_array_t &aPrograms) const
	{
		CHECK_ERR( _uID != 0 );
		
		aPrograms.ClearMem();

		for (gl_shader::index i = gl_shader::_IDX_FIRST; i < gl_shader::_IDX_COUNT; ++i)
		{
			GLenum	e_type	= gl_shader::IndexToType( i );
			GLint	i_res	= 0;
			bool	b_res	= true;
			GL_CALL_B( glGetProgramPipelineiv( _uID, e_type, &i_res ), b_res );

			if ( !b_res )
				continue;
			
			FOR( j, aPrograms )
			{
				if ( aPrograms[j].uID == i_res ) {
					aPrograms[j].eShaderBits |= gl_shader::IndexToBits( i );
					b_res = false;
					break;
				}
			}

			if ( b_res )
				aPrograms.PushBack( GLAttachedPrograms( i_res, gl_shader::IndexToBits( i ) ) );
		}
		return true;
	}

//-------------------------------------------------------------------
	
}	// GLShell
}	// UX_STL